﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using LookupField.Models;

namespace LookupField.Helpers
{
    public static class LookupHtmlHelper
    {
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcHtmlString LookupBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string associationProperty, string displayMember = null, string format = null, IDictionary<string, object> htmlAttributes = null)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            return LookupBoxHelper(htmlHelper,
                                 metadata,
                                 metadata.Model,
                                 associationProperty,
                                 ExpressionHelper.GetExpressionText(expression),
                                 displayMember,
                                 format,
                                 htmlAttributes);
        }

        private static MvcHtmlString LookupBoxHelper(this HtmlHelper htmlHelper, ModelMetadata metadata, object model, string associationProperty, string expression, string displayMember, string format, IDictionary<string, object> htmlAttributes)
        {
            return InputHelper(htmlHelper,
                               InputType.Text,
                               metadata,
                               expression,
                               model,
                               associationProperty,
                               displayMember,
                               format: format,
                               htmlAttributes: htmlAttributes);
        }

        private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, string associationProperty, string displayMember, string format, IDictionary<string, object> htmlAttributes)
        {

            string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
            if (String.IsNullOrEmpty(fullName))
            {
                throw new ArgumentException("Value cannot be null or empty.", "name");
            }

            TagBuilder tagBuilder = new TagBuilder("input");
            tagBuilder.MergeAttributes(htmlAttributes);
            tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
            tagBuilder.MergeAttribute("name", fullName, true);

            string valueParameter = htmlHelper.FormatValue(value, format);

            string attemptedValue = (string)GetModelStateValue(htmlHelper, fullName, typeof(string));
            tagBuilder.MergeAttribute("value", attemptedValue ?? valueParameter, true);

            tagBuilder.GenerateId(fullName);

            // If there are any errors for a named field, we add the css attribute.
            ModelState modelState;
            if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
            {
                if (modelState.Errors.Count > 0)
                {
                    tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
                }
            }

            tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));

            StringBuilder result = new StringBuilder();
            result.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));


            string description = null;

            //if (displayMember == null)
            //    description = value.ToString();
            //else
            //{
            //    var t = value.GetType();
            //    var p = t.GetProperty(displayMember);

            //    if (p == null)
            //        throw new ArgumentException(String.Format("The lookup value does´t contains a property named {0}.", displayMember), "displayMember");

            //    var displayValue = p.GetValue(value, null);

            //    description = htmlHelper.FormatValue(displayValue, format);
            //}

            var txtBox = new TagBuilder("input");
            txtBox.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Text));
            txtBox.MergeAttribute("name", fullName + "-display-field", true);
            txtBox.MergeAttribute("value", attemptedValue != null ? "" : description, true);

            var lookup = new LookupModel();
            lookup.HiddenId = tagBuilder.Attributes["name"];
            lookup.HiddenValue = tagBuilder.Attributes["value"];

            lookup.DisplayId = txtBox.Attributes["name"];
            lookup.DisplayValue = txtBox.Attributes["value"];

            lookup.LinkId = tagBuilder.Attributes["name"] + "-lookup";

            lookup.Columns = GetCollumns(metadata.ContainerType, associationProperty);

            lookup.ClassType = metadata.ContainerType.GetProperty(associationProperty).PropertyType.ToString();

            return htmlHelper.Partial("~/Views/Shared/Lookup/_LookupField.cshtml", lookup);
        }

        private static Dictionary<string, string> GetCollumns(Type modelType, string associationProperty)
        {
            var result = new Dictionary<string, string>();

            var association = modelType.GetProperty(associationProperty);

            if (String.IsNullOrEmpty(associationProperty))
            {
                throw new ArgumentException("Value cannot be null or empty.", "associationProperty");
            }

            var associationType = association.PropertyType;

            var props = associationType.GetProperties().Where(p => p.CustomAttributes.Count(c => c.AttributeType.Name.Equals("LookupListAttribute")) > 0);
            foreach (var p in props)
            {
                var attribute = p.CustomAttributes.FirstOrDefault(c => c.AttributeType == typeof(DisplayAttribute));
                if (attribute != null && attribute.NamedArguments != null)
                {
                    var name = attribute.NamedArguments.FirstOrDefault(c => c.MemberName.Equals("Name")).TypedValue.Value as string;
                    result.Add(p.Name, name);
                }
                else
                {
                    result.Add(p.Name, p.Name);
                }
            }

            return result;
        }

        internal static object GetModelStateValue(HtmlHelper helper, string key, Type destinationType)
        {
            ModelState modelState;
            if (helper.ViewData.ModelState.TryGetValue(key, out modelState))
            {
                if (modelState.Value != null)
                {
                    return modelState.Value.ConvertTo(destinationType, null /* culture */);
                }
            }
            return null;
        }
    }
}
